package bookhive;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import bookhive.datastructures.catalog.CatalogGroupInfo;

import net.jxta.discovery.DiscoveryService;
import net.jxta.exception.PeerGroupException;
import net.jxta.peergroup.PeerGroup;
import net.jxta.platform.NetworkConfigurator;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.PeerGroupAdvertisement;
import net.jxta.rendezvous.RendezVousService;

public abstract class PeerLauncher {

	private static final long GroupDiscoveryDelay = 2000;

	protected NetworkConfigurator netconfig;

	protected URI jxtaHome = null;

	protected TreeSet<CatalogGroupInfo> catalogGroups = new TreeSet<CatalogGroupInfo>();

	protected NetworkManager manager = null;

	protected Properties catalogProps;

	public void groupsInit() {

		File catalogGroupsFile = new File("config/CatalogGroups.xml");

		if (catalogGroupsFile.exists()) {
			try {
				Document catalogGroupsXml = DocumentBuilderFactory
						.newInstance().newDocumentBuilder().parse(
								catalogGroupsFile);
				NodeList nodes = catalogGroupsXml.getChildNodes();
				nodes = nodes.item(0).getChildNodes();
				for (int i = 0; i < nodes.getLength(); i++) {
					Node node = nodes.item(i);
					if (node.getNodeName().equals("CatalogGroup")) {
						String gName = "";
						TreeSet<URI> gSupers = new TreeSet<URI>();
						NodeList groupNodes = node.getChildNodes();
						for (int j = 0; j < groupNodes.getLength(); j++) {
							Node attr = groupNodes.item(j);
							if (attr.getNodeName().equals("Name"))
								gName = attr.getChildNodes().item(0)
										.getNodeValue();
							else if (attr.getNodeName().equals("SuperNode")) {
								gSupers.add(new URI(attr.getChildNodes()
										.item(0).getNodeValue()));
							}
						}
						catalogGroups.add(new CatalogGroupInfo(gName, gSupers));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		Iterator<CatalogGroupInfo> gInfoIter = this.catalogGroups.iterator();
		while (gInfoIter.hasNext()) {
			CatalogGroupInfo gInfo = gInfoIter.next();
			Iterator<URI> uriIter = gInfo.getSuperNodes().iterator();
			while (uriIter.hasNext()) {
				try {
					manager.getConfigurator().addSeedRendezvous(uriIter.next());
				} catch (IOException e) {
					System.out.println(e.getMessage());
				}
			}
		}

	}

	public PeerLauncher(File configFile) {
		readCatalogConfigFile(configFile);
		netconfigInit();
		groupsInit();
	}

	public PeerLauncher() {
		netconfigInit();
		groupsInit();
	}

	public PeerLauncher(TreeSet<CatalogGroupInfo> groups) {
		catalogGroups.addAll(groups);
		netconfigInit();
		groupsInit();
	}

	public void netconfigInit() {
		try {
			String cacheDir = catalogProps.getProperty("CacheDir");
			manager = new NetworkManager(NetworkManager.ConfigMode.EDGE, "");
			netconfig = manager.getConfigurator();
			String homeFileName = netconfig.getHome().getAbsolutePath();
			if (cacheDir != null && cacheDir.length() > 0)
				homeFileName = homeFileName.replaceAll("\\.jxta", cacheDir);
			homeFileName = homeFileName.replaceAll("\\\\", "/");

			netconfig.setHome(new File(homeFileName));
			netconfig.setStoreHome(URI.create("file:///" + homeFileName));
			manager.setInstanceHome(URI.create("file:///" + homeFileName));
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public PeerGroup joinGroupIfExists(CatalogGroupInfo gInfo) throws Exception {

		boolean connected = false;

		RendezVousService rdvSvc = manager.getNetPeerGroup()
				.getRendezVousService();
		Thread.sleep(2000);

		connected = rdvSvc.isRendezVous() || rdvSvc.isConnectedToRendezVous();
		if (!connected) {
			for (int i = 0; i < 3; i++) {

				rdvSvc = manager.getNetPeerGroup().getRendezVousService();

				manager.waitForRendezvousConnection(2000);
				connected = rdvSvc.isConnectedToRendezVous();

				if (connected) {
					Enumeration connRdvs = rdvSvc.getConnectedRendezVous();
					System.out.println("-- RDVs -- ");
					while (connRdvs.hasMoreElements()) {
						System.out.println("RDV: " + connRdvs.nextElement());
					}
					break;
				}
			}
		}
		System.out.println("PeerLauncher: connected = " + connected);

		String attr = "Name";
		int threshold = 10;
		PeerGroup tpGroup = null;

		if ((gInfo.getName() == null) || (gInfo.getName().length() == 0))
			return null;
		DiscoveryService discovery = manager.getNetPeerGroup()
				.getDiscoveryService();
		Enumeration res = null;
		for (int i = 0; i < 5; i++) {// sent remote group discovery message
			discovery.getRemoteAdvertisements(null, DiscoveryService.GROUP,
					attr, gInfo.getName(), threshold, null);
			// wait 5 sec
			// System.out.println("remote group discovery message sent");
			Thread.sleep(GroupDiscoveryDelay);
			// check local cache
			res = discovery.getLocalAdvertisements(DiscoveryService.GROUP,
					attr, gInfo.getName());
			if (res != null && res.hasMoreElements()) {
				System.out.println("res has more elements");
				break;
			}
		}

		PeerGroupAdvertisement groupAdv = null;
		if (res != null && res.hasMoreElements()) {
			groupAdv = (PeerGroupAdvertisement) res.nextElement();

			// create group with advertisement
			try {
				tpGroup = manager.getNetPeerGroup().newGroup(groupAdv);
			} catch (PeerGroupException e) {
				e.printStackTrace();
			}

			System.out.println("Group " + tpGroup.getPeerGroupName()
					+ " joined successfully");
		} else
			System.out.println("Group not found");
		return tpGroup;

	}

	private void readCatalogConfigFile(File catalogConfigFile) {

		catalogProps = new Properties();
		try {
			catalogProps.load(new FileInputStream(catalogConfigFile));
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
